package com.cfpl.security;

import com.cfpl.filter.JWTAuthenticationTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * @Auther: LYP
 * @Date: 2023/10/12 - 10:46
 * @Description: Security配置类
 * @version: 1.0
 */
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{

    @Autowired
    private MyAuthenticationEntryPoint myAuthenticationEntryPoint;
    @Autowired
    private MyAccessDeniedHandler myAccessDeniedHandler;

    /**
     * 重写这个方法就不存在登陆成功或者失败处理器了，
     * 没有这个 super.configure();
     *  就不存在 UsernamePasswordAuthenticationFilter 也就不会走spring security的认证流程（不出现SpringSecurity的默认登录页面） 也就不会在认证成功后走登录成功处理器
     *  那些默认的请求认证也不存在了，需要手动配置那些请求需要认证和放行
     *          http.authorizeRequests((requests) -> {
     *             ((ExpressionUrlAuthorizationConfigurer.AuthorizedUrl)requests.anyRequest()).authenticated();
     *         });
     * @param http
     * @throws Exception
     */
    @Autowired
    protected void configure(HttpSecurity http) throws Exception {
//        super.configure();
        //关闭csrf防护
        /**
         *不通过Session获取SecurityContext    为什么不携带token还能进行访问的原因就是这个配置
         * SecurityContextPersistenceFilter是Springsecurity链中第二道防线，位于WebAsyncManagerIntegrationFilter之后，作用是为了存储SecurityContext而设计的。
         * SecurityContextPersistenceFilter主要做两件事：
         *
         * 当请求到来时，从HttpSession中获取SecurityContext并存入SecurityContextHolder中，这样在同一个请求的后续处理过程中，通过SecurityContextHolder获取数据
         * 当一个请求处理完毕时，从SecurityContextHolder中获取SecurityContext并存入HttpSession中，方便下一个请求到来时，再从HTTPSession中拿来使用，同时擦除SecurityContextHolder中的登录信息。
         *
         */
        http.csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                //对于登录接口允许匿名访问
                .antMatchers("/backStage/admin/login").anonymous();


        //接口对应的权限信息
        http.authorizeRequests()
                //管理员： (管理员、角色、权限信息)，种植户信息，经销商信息
                .antMatchers("/admin/**","/growerInfo/**","/dealerAdvice/**").hasAnyAuthority("/PersonnelDept/showAll")
                //培育人员权限管理	： 肥料使用管理、农药使用管理、果蔬基础信息、果树管理信息 、 种植地信息
                .antMatchers("/fertilizer/**","/pesticide/**","/vegetable/**","/vegetableManage/**",
                        "/plantingLocation/**","/vegVariety/**").hasAnyAuthority("/CultivatePersonnel/showAll")
                //检测人员 : 环境控制管理 、 预警管理 、 质量检测
                .antMatchers("/envMonitoring/**","/warning/**","/qualityInspectionRecord/**").hasAnyAuthority("/DetectionPersonnel/showAll")
                //仓库管理人员 :   仓储管理、果蔬进场、分发商品
                .antMatchers("/vegetableEntry/**","/warehouse/**","/distribute").hasAnyAuthority("/StockRoomPersonnel/showAll");


        //权限拦截配置
        http.authorizeRequests()
                .antMatchers("/backStage/admin/login").permitAll()       //接口不需要认证权限
                .antMatchers("/ws/**").permitAll()
                //放行接口文档
                .antMatchers("/doc.html/**").permitAll()
                .antMatchers("/api/**").permitAll()
                //放行接口文档
                .antMatchers("/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**","/favicon.ico").permitAll()
                .anyRequest().authenticated();                                      //其余接口都需要认证权限



        //退出登录
//        http.logout()
//                .logoutUrl("/backStage/admin/logout")
//                .logoutSuccessHandler(new MyLogoutSuccessHandler())
//                .invalidateHttpSession(true)
//                .clearAuthentication(true)
//                .deleteCookies("JSESSIONID");   //退出后删除cookie

        //异常处理
        http.exceptionHandling()
                .authenticationEntryPoint(myAuthenticationEntryPoint)             //用户未登录
                .accessDeniedHandler(myAccessDeniedHandler);                      //权限不足处理器


        //添加过滤器 在哪个过滤器之前   UsernamePasswordAuthenticationFilter这个过滤器是最前面的 要在这个之前设置自定义过滤器
        http.addFilterBefore(new JWTAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);

        //开启跨域
        http.cors();
    }

    /**
     * 自定义认证
     * @return
     * @throws Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }


//    自定义的基于JDBC的用户认证
    @Bean
    public UserDetailsService userDetailsService(){
        return new AdminDetailService();
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //配置认证方式
        auth.userDetailsService(userDetailsService());
    }

    /**
     * 加密对象
     * @return BCryptPasswordEncoder
     */
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }


}
